# -*- coding:utf-8 -*-
__author__ = 'Administrator'
__all__ = ('register_animate', 'animate_factory', 'AnimateProxy',)
from kivy.clock import Clock
from kivy.animation import  AnimationTransition
from composite_widget.transform import get_origin_value
from array import array
from copy import deepcopy
from functools import wraps

#参数bezier曲线点
#coordinates 4个控制点
#输出（x，y）点序列
def _generate_bezier_points(coordinates, segments=45):
    points_x = array('f')
    points_y = array('f')
    T = coordinates[:]
    for x in xrange(segments):
        l = x / (1.0 * segments)
        for i in range(1, len(T)):
            for j in xrange(len(T) - 2*i):
                T[j] = T[j] + (T[j+2] - T[j]) * l
        # we got the coordinates of the point in T[0] and T[1]
        points_x.append(T[0])
        points_y.append(T[1])
    points_x.append(T[-2])
    points_y.append(T[-1])
    return (points_x, points_y)

class CubicBezier(object):
    def __init__(self, x1, y1, x2, y2):
        self.point_x = None
        self.point_y = None
        self.point_x, self.point_y = _generate_bezier_points([0,0,x1, y1, x2, y2,1.0,1.0])
        super(CubicBezier, self).__init__()

    def transition(self, progress, old_index):
        y = 1.0
        new_index = old_index
        px,py = self.point_x,self.point_y
        for new_index, t in enumerate(px[old_index: -1], old_index):
            t_next = px[new_index+1]
            if progress <= t_next:
                y1 = py[new_index]
                y2 = py[new_index + 1]
                sc = (progress - t) / (t_next - t)
                val = sc*y2 + (1 - sc)*y1
                return (val, new_index)
        return (y, new_index)

def transition(func):
    @wraps(func)
    def wrapper(progress, old_index):
        return (func(progress), old_index)
    return wrapper
_linear_time_func = CubicBezier(0.0, 0.0, 1.0, 1.0)
_ease_time_func = CubicBezier(0.25, 0.1, 0.25, 1.0)
_ease_in_time_func = CubicBezier(0.42, 0, 1.0, 1.0)
_ease_out_time_func = CubicBezier(0, 0, 0.58, 1.0)
_ease_in_out_time_func = CubicBezier(0.42, 0, 0.58, 1.0)
def _get_time_func(value):
    if isinstance(value, str):
        if value == 'ease':
            global _ease_time_func
            return _ease_time_func.transition
        elif value == 'linear':
            global _linear_time_func
            return _linear_time_func.transition
        elif value == 'ease-in':
            global _ease_in_time_func
            return _ease_in_time_func.transition
        elif value == 'ease-out':
            global _ease_out_time_func
            return _ease_out_time_func.transition
        elif value == 'ease-in-out':
            global _ease_in_out_time_func
            return _ease_in_out_time_func.transition
        else:
            return transition(getattr(AnimationTransition, value))
    elif isinstance(value, tuple):
        cub_z = CubicBezier(*value)
        return cub_z.transition
    return  transition(AnimationTransition.linear)

class AnimateTransform(object):
    name = ''
    def __init__(self,**kwargs):
        #{'duration':2.0, 'delay':0.5, 'keyframes':[((0, 0.2, 0.4, 1),{}),(keys, properties)]}
         #{'time_func':'', 'opacity':0.6, 'scale':{'x':1.0,'y':1.0},  'rotate':{'angle':30,'origin':()}, 'translate':{}}
        self._duration = 1.0
        self._delay = 0
        self._keys_data ={}
        self._keys_state = {}
        #时间函数对象列表
        self._time_funcs=[]

        #平移时是否使用百分比单位
        self._use_percent_unit = True
        super(AnimateTransform, self).__init__()
        self._parse(**kwargs)

    @property
    def duration(self):
        return self._duration

    @property
    def delay(self):
        return self._delay

    @property
    def keys_data(self):
        return self._keys_data

    @property
    def keys_state(self):
        return self._keys_state

    @property
    def time_funcs(self):
        return self._time_funcs

    @property
    def use_percent_unit(self):
        return self._use_percent_unit

    def _parse(self, **kwargs):
        data = {}
        self._duration = kwargs.get('duration', 1.0)
        self._delay = kwargs.get('delay', 0)
        keyframes = kwargs.get('keyframes', [])
        for keys, properties in keyframes:
            if isinstance(keys, tuple):
                for key in keys:
                    if not data.has_key(key):
                        data[key] = {}
                    data[key].update(properties)
            else:
                if not data.has_key(keys):
                    data[keys] = {}
                data[keys].update(properties)
        if len(data) < 2:
            raise Exception('animate must have 2 keyframe data')
        if not data.has_key(0.0) or not data.has_key(1.0):
            raise Exception('animate must have key=0.0(from) and key=1.0(to)')
        #转换成如下数据格式
        #连续变化数据的属性
        #keys_data_temp = {0.1:{'time_func':callable, 'opacity':1.0, 'rotate_angle':0 ,'scale':(x,y),'translate':(x,y), 'color':(r,g,b)}, 0.2:...}
        #状态切换的属性
        #keys_state = {0:{'rotate_axis':(0,0,1), 'rotate_origin':(0,0), 'scale_origin':(0,0), key:words}, 1:{}}
        keys_data_temp = {}
        self._keys_state.clear()
        self._keys_data.clear()
        for key, value in data.items():
            rotate_dt = scale_dt = translate_dt = None
            func = None
            kw_properties = {}
            k_p_dict = self._keys_state[key] = {}
            for ppt, vl in value.items():
                if ppt == 'time_func':
                    func = _get_time_func(vl)
                    kw_properties.update({'time_func':func})
                elif ppt == 'opacity':
                    kw_properties.update({'opacity':vl})
                elif ppt == 'rotate':
                    rotate_dt = vl
                elif ppt == 'scale':
                    scale_dt = vl
                elif ppt == 'translate':
                    translate_dt = vl
                elif ppt == 'color':
                    kw_properties.update({'color':vl})
                elif ppt == 'size':
                    #转换成scale_dt
                    pass
                elif ppt == 'pos':
                    #转换成translate_dt
                    pass
                elif ppt == 'center_pos':
                    #转换成translate_dt
                    pass
                else:
                    #其它的属性,其它属性都为状态切换数据
                    k_p_dict.update({ppt:vl})
            if func is None:
                func = _get_time_func('ease')
                kw_properties.update({'time_func':func})
            if rotate_dt:
                rotate_angle = rotate_dt.get('angle', 0)
                rotate_axis = rotate_dt.get('axis',None)
                if rotate_axis:
                    k_p_dict.update({'rotate_axis':rotate_axis})
                rotate_origin = rotate_dt.get('origin',None)
                if rotate_origin:
                    k_p_dict.update({'rotate_origin':rotate_origin})
                kw_properties.update({'rotate_angle': rotate_angle})
            if scale_dt:
                scale_x = scale_dt.get('x', 1.0)
                scale_y = scale_dt.get('y', 1.0)
                scale_origin =scale_dt.get('origin', None)
                if scale_origin:
                    k_p_dict.update({'scale_origin':scale_origin})
                kw_properties.update({'scale':(scale_x,scale_y)})

            if translate_dt:
                translate_x = translate_dt.get('x', 0)
                translate_y = translate_dt.get('y', 0)
                if translate_x > 1 or translate_y > 1:
                    self._use_percent_unit = False
                kw_properties.update({'translate':(translate_x,translate_y)})
            keys_data_temp[key] = kw_properties

        #连续变化数据的属性转换成方便计算和访问的格式(每个时间段带有开始和结束的值)
        keys = keys_data_temp.keys()
        keys.sort()
        keys_num = len(keys)
        last_opacity = 1.0
        last_angle = 0
        last_scale = (1.0,1.0)
        last_translate =(0,0)
        last_color = (1,1,1)
        key_properties = ['opacity', 'rotate_angle','scale','translate','color']
        key_last_val = [last_opacity, last_angle, last_scale, last_translate, last_color]
        key_funcs = self._time_funcs
        for i in range(keys_num - 1):
            key_data = {}
            start_time = keys[i]
            start_kw = keys_data_temp[start_time]
            end_time = keys[i + 1]
            end_kw = keys_data_temp[end_time]
            key_funcs.append(start_kw['time_func'])
            for j in range(5):
                key_string = key_properties[j]
                last_val = key_last_val[j]
                if end_kw.has_key(key_string):
                    end_val = end_kw[key_string]
                    if start_kw.has_key(key_string):
                        start_val = start_kw[key_string]
                        key_data.update({key_string:(start_val, end_val)})
                    else:
                        key_data.update({key_string:(last_val, end_val)})
                    key_last_val[j] = end_val
            self._keys_data[start_time] = key_data
        #print '----keys stats------'
        #print self._keys_state

class AnimateProxy(object):
    def __init__(self, animate_transform):
        self._dt = 0

        #内容的大小
        self._c_size = (0,0)

        #内容的位置
        self._c_pos = (0,0)

        self._cur_time_func = None

        self._repeat = 1

        self._cur_key_frame_index = 0

        #关键帧的时间，百分比单位
        self._key_frames = None

        #关键帧的时间长（由总时间和百分比分算出）
        self._time_frames = None

        #关键帧个数=时间点个数-1
        self._key_frames_len = 0
        self._content = None
        self._t_process_index = 0
        super(AnimateProxy,self).__init__()
        self._duration = animate_transform.duration
        self._delay = animate_transform.delay
        #时间函数列表
        self._time_funcs = animate_transform.time_funcs
        #平移时是否使用百分比单位
        self._use_percent_unit = animate_transform.use_percent_unit
        self._keys_data = deepcopy(animate_transform.keys_data)
        self._keys_state = deepcopy(animate_transform.keys_state)
        self._origin = {'rotate':{}, 'scale':{}, 'translate': {}}
        self._key_frames = self._keys_state.keys()
        self._key_frames.sort()

    def reset(self):
        self._dt = 0
        self._cur_key_frame_index = -1
        self._t_process_index = 0
        self._cur_time_func = self._time_funcs[0]
        content = self._content
        c_x = content._x
        c_y = content._y
        c_size = content._size[:]
        if self._c_pos != (c_x, c_y) or self._c_size != c_size:
            self._c_size = c_size[:]
            self._c_pos = (c_x, c_y)
            #print self._c_size
            #print self._c_pos
            #把原点字符串或百分比转换成实际值
            rotate_origin = self._origin['rotate']
            scale_origin = self._origin['scale']
            translate = self._origin['translate']
            for k, w in self._keys_state.items():
                if w.has_key('rotate_origin'):
                    if not rotate_origin.has_key(k):
                        rotate_origin[k] = w['rotate_origin']
                    origin = rotate_origin[k]
                    w['rotate_origin'] = get_origin_value(origin, content)
                if w.has_key('scale_origin'):
                    if not scale_origin.has_key(k):
                        scale_origin[k] = w['scale_origin']
                    origin = scale_origin[k]
                    w['scale_origin'] = get_origin_value(origin, content)
            if self._use_percent_unit:
                #把平移的百分比单位转成像素单位
                for kt in self._key_frames:
                    if kt == 1.0:
                        continue
                    ppt = self._keys_data[kt]
                    if ppt.has_key('translate'):
                        if not translate.has_key(kt):
                            translate[kt] = ppt['translate']
                        begin, end = translate[kt]
                        x1,y1 = begin
                        x1 = x1*c_size[0]
                        y1 = y1*c_size[1]
                        x2,y2 = end
                        x2 = x2*c_size[0]
                        y2 = y2*c_size[1]
                        ppt['translate'] = (x1,y1),(x2,y2)
            #print '-----last--keys_data-'
            #print self._keys_data
            #print '----keys stats------'
            #print self._keys_state

    def start(self, repeat):
        self._repeat = repeat
        self.reset()
        #把第一次运行， 设置为-2
        self._cur_key_frame_index = -2


    def bind(self, content, duration=None, delay=None):
        #动画和内容对象进行绑定
        self._content = content
        if content is None:
            return
        if duration:
            self._duration = duration
        if delay:
            self._delay = delay
        dur = self._duration
        self._time_frames = array('f',[b*dur for b in self._key_frames])
        self._key_frames_len = len(self._key_frames) - 1
        self.reset()
        #强制在内容的指令组里添加变换矩阵组
        content.transform = {}

    def _get_key_frame_index_at(self, t, cur_key, time_frames):
        start_index = cur_key
        time_frames = time_frames
        for index, _val in enumerate(time_frames[start_index: -1], start_index):
            if t < time_frames[index+1]:
                return index
        else:
            return cur_key

    def _set_cur_state_in(self, keys_state, key_frames, old_index, new_index):
        key_tm = key_frames[new_index]
        spans = new_index - old_index
        if spans > 1:
            new_states = {}
            new_states.update(keys_state[key_tm])
            #把跳过的关键帧中属性重新补上
            for i in range(spans):
                skip_key_frame = key_frames[new_index - i]
                skip_states = keys_state[skip_key_frame]
                for key, val in skip_states.items():
                    if not new_states.has_key(key):
                        new_states[key] = val
            return new_states
        else:
            return keys_state[key_tm]

    def _calculate(self, a, b, t):
        _calculate = self._calculate
        if isinstance(a, tuple):
            return tuple([_calculate(a[x], b[x], t) for x in range(len(a))])
        else:
            if a == b:
                return a
            return (a * (1. - t)) + (b * t)

    def stop(self):
        content = self._content
        calculate = self._calculate
        #把最后状态更新上去
        _index = self._key_frames_len - 1
        cur_key_index = self._cur_key_frame_index
        key_frames = self._key_frames
        keys_state = self._keys_state
        self._cur_time_func = self._time_funcs[_index]
        if cur_key_index < _index:
            set_state = self._set_cur_state_in(keys_state, key_frames, cur_key_index, _index)
            for key,val in set_state.items():
                setattr(content,key,val)
                #print 'set key state attr {}={}'.format(key, val)
        key_tm = key_frames[-2]
        cur_data = self._keys_data[key_tm]
        #设置最后的数据属性
        for key,val in cur_data.items():
            t_v = calculate(val[0], val[1], 1)
            setattr(content, key, t_v)
            #print 'set key data attr {}={}'.format(key,t_v)

    def update(self, dt):

        self._dt += dt

        if self._dt < self._delay:
            return False

        content = self._content
        calculate = self._calculate
        if self._dt >= self._duration:
            self._repeat -= 1
            if self._repeat <= 0:
                #停止动画
                self.stop()
                return True
            else:
                self.reset()
                self._dt += dt
                if self._dt < self._delay:
                    return False

        run_dt = self._dt - self._delay
        #更换关键帧
        cur_key_index = self._cur_key_frame_index
        _index = self._get_key_frame_index_at(run_dt, max(cur_key_index, 0), self._time_frames)
        key_frames = self._key_frames
        keys_state = self._keys_state
        if cur_key_index < _index:
            self._cur_time_func = self._time_funcs[_index]
            self._t_process_index = 0
            set_state = self._set_cur_state_in(keys_state, key_frames, cur_key_index, _index)
            for key,val in set_state.items():
                setattr(content,key,val)
                #print 'set key state attr {}={}'.format(key, val)
            if cur_key_index == -2:
                set_data = self._keys_data[0]
                for key,val in set_data.items():
                    setattr(content, key, val[0])
                    #print 'set key data attr {}={}'.format(key,val[0])

        self._cur_key_frame_index = _index
        cur_time_func = self._cur_time_func
        t_process_index = self._t_process_index
        time_frames = self._time_frames
        key_tm = key_frames[_index]
        cur_data = self._keys_data[key_tm]
        #设置连续变化的属性
        process = (run_dt - time_frames[_index]) / (time_frames[_index + 1] - time_frames[_index])
        t_process, t_process_index = cur_time_func(process, t_process_index)
        self._t_process_index = t_process_index
        for key,val in cur_data.items():
            t_v = calculate(val[0], val[1], t_process)
            setattr(content, key, t_v)
            #print 'set key data attr {}={}'.format(key,t_v)
        return False

#常用动画数据参数
#摇晃动画
_shake_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'translate':{'x':0}}),\
             ((0.1,0.3,0.5,0.7,0.9),{'translate':{'x':-10}}), \
             ((0.2,0.4,0.6,0.8),{'translate':{'x':10}}), \
             (1.0,{'translate':{'x':0}})]}
#闪烁动画
_flash_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'opacity':1.0}),\
             (0.5,{'opacity':1.0}),\
             ((0.25, 0.75),{'opacity':0.0}), \
             (1.0,{'opacity':1.0})]}
#脉冲
_pulse_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'scale':{'x':1.0, 'y':1.0}}),\
             (0.5,{'scale':{'x':1.05, 'y':1.05}}), \
             (1.0,{'scale':{'x':1.0, 'y':1.0}})]}
#弹跳
_bounce_ = {}

#晃动
_wobble_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'translate':{'x':0}, 'rotate':{'angle': 0}}),\
             (0.15,{'translate':{'x':-0.25}, 'rotate':{'angle': -5}}), \
             (0.30,{'translate':{'x':0.2}, 'rotate':{'angle': 3}}), \
             (0.45,{'translate':{'x':-0.15}, 'rotate':{'angle': -3}}), \
             (0.6,{'translate':{'x':0.1}, 'rotate':{'angle': 2}}), \
             (0.75,{'translate':{'x':-0.05}, 'rotate':{'angle': -1}}), \
             (1.0,{'translate':{'x':0}, 'rotate':{'angle': 0}})]}
#摆动
_swing_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'rotate':{'angle': 0, 'origin':'top_center'}}),\
             (0.2,{'rotate':{'angle': 15}}), \
             (0.4,{'rotate':{'angle': -10}}), \
             (0.6,{'rotate':{'angle': 5}}), \
             (0.8,{ 'rotate':{'angle': -5}}), \
             (1.0,{'rotate':{'angle': 0}})]}
#拉橡皮筋
_rubberBand_ ={'duration':1.0,  'keyframes':[ \
             (0.0,{'scale':{'x':1.0, 'y':1.0}}),\
             (0.3,{'scale':{'x':1.25, 'y':0.75}}), \
             (0.4,{'scale':{'x':0.75, 'y':1.25}}),\
             (0.5,{'scale':{'x':1.15, 'y':0.85}}), \
             (0.65,{'scale':{'x':0.95, 'y':1.05}}), \
             (0.75,{'scale':{'x':1.05, 'y':0.95}}), \
             (1.0,{'scale':{'x':1.0, 'y':1.0}})]}

#惊讶
_tada_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'scale':{'x':1.0, 'y':1.0}, 'rotate':{'angle': 0}}),\
             ((0.1,0.2),{'scale':{'x':0.9, 'y':0.9}, 'rotate':{'angle': -3}}), \
             ((0.3,0.5,0.7,0.9),{'scale':{'x':1.1, 'y':1.1}, 'rotate':{'angle': 3}}), \
             ((0.4,0.6,0.8),{'scale':{'x':1.1, 'y':1.1}, 'rotate':{'angle': -3}}), \
             (1.0,{'scale':{'x':1.0, 'y':1.0}, 'rotate':{'angle': 0}})]}
#entery
_zoomInDown_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'opacity':0.0, 'scale':{'x':0.1, 'y':0.1}, 'translate':{'y':1000}, 'time_func':(0.550, 0.055, 0.675, 0.190)}),\
             (0.6,{'opacity':1.0, 'scale':{'x':0.475, 'y':0.475}, 'translate':{'y':-60},'time_func':(0.175, 0.885, 0.320, 1.0)}), \
             (1.0,{'scale':{'x':1.0, 'y':1.0},'translate':{'y':0}})]}
#exit
_zoomOutUp_ = {'duration':1.0,  'keyframes':[ \
             (0.0,{'opacity':1.0, 'scale':{'x':1.0, 'y':1.0}, 'translate':{'y':0}}),\
             (0.4,{'scale':{'x':0.475, 'y':0.475}, 'translate':{'y':-60},'time_func':(0.175, 0.885, 0.320, 1.0)}), \
             (1.0,{'opacity':0.0, 'scale':{'x':0.1, 'y':0.1},'translate':{'y':2000}, 'time_func':(0.550, 0.055, 0.675, 0.190)})]}

_animate_obj = {}
def register_animate(name, **kwargs):
    global  _animate_obj
    if not _animate_obj.has_key(name):
        _animate_obj[name] = AnimateTransform(**kwargs)

def animate_factory(name):
    global  _animate_obj
    if not _animate_obj.has_key(name):
        if name == 'shake':
            register_animate(name, **_shake_)
        elif name == 'flash':
            register_animate(name, **_flash_)
        elif name == 'pulse':
            register_animate(name, **_pulse_)
        elif name == 'bounce':
            register_animate(name, **_bounce_)
        elif name == 'wobble':
            register_animate(name, **_wobble_)
        elif name == 'swing':
            register_animate(name, **_swing_)
        elif name == 'rubberBand':
            register_animate(name, **_rubberBand_)
        elif name == 'tada':
            register_animate(name, **_tada_)
        elif name == 'zoomInDown':
            register_animate(name, **_zoomInDown_)
        elif name == 'zoomOutUp':
            register_animate(name, **_zoomOutUp_)
        else:
            raise Exception('animate {} is not register'.format(name))
    return AnimateProxy(_animate_obj[name])
